#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>

int open_and_init(const char *path) {
    struct termios cfg;
    speed_t speed;
    int fd;

    memset(&cfg, 0, sizeof(struct termios));
    fd = open(path, O_RDWR);
    if (fd < 0)  return -1;

    speed = B115200;

    if (tcgetattr(fd, &cfg)) {
        printf("tcgetattr fail\n");
        return -1;
    }

    cfmakeraw(&cfg);
    cfsetispeed(&cfg, speed);
    cfsetospeed(&cfg, speed);

    if (tcsetattr(fd, TCSANOW, &cfg)) {
        printf("tcsetattr fail\n");
        close(fd);
    }

    return fd;
}

ssize_t _read_line(int fd, char buf[], int buf_size) {
    ssize_t read_count = 0;

    memset(buf, 0, buf_size);

    while (read_count < buf_size - 1) {
        ssize_t read_ret = read(fd, buf+read_count, 1);
        if (read_ret < 0) {
            return read_ret;
        } else if (read_ret == 0) {
            break;
        } else {
            if (buf[read_count] == '\n') {
                read_count++;
                break;
            }

            read_count++;
        }
    }

    return read_count;
}

void *read_and_echo_port(void *port) {
    int fd = (int) port;
    char buf[1] = {0};

    while (1) {
        ssize_t ret = read(fd, buf, 1);
        if (ret < 0) {
            break;
        } else {
            write(STDOUT_FILENO, buf, 1);
        }
    }


    return NULL;
}

void read_stdin_and_write(int fd) {
    char buf[1024] = {0};

    while (1) {
        ssize_t ret = _read_line(STDIN_FILENO, buf, sizeof(buf));
        if (ret < 0) {
            printf("read stdin fail (%d)\n", ret);
            return;
        }

        ssize_t write_ret = write(fd, buf, ret);
        if (write_ret < 0) {
            printf("write port fail: (%d)\n", write_ret);
            return;
        }
    }
}

int main(int argc, char **argv) {
    int fd = open_and_init(argv[1]);
    if (fd < 0) {
        printf("open %s fail\n", argv[1]);
        return -1;
    }

    pthread_t tid;

    pthread_create(&tid, NULL, read_and_echo_port, (void *)fd);

    read_stdin_and_write(fd);

    return 0;
}